package org.gdstash.item;

import org.gdstash.file.GDReader;
import org.gdstash.file.GDWriter;
import org.gdstash.util.GDLog;
import org.gdstash.util.GDMsgLogger;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;

public class GDStashPage extends GDAbstractContainer {
    private InputStream reader;
    private String filename;
    private int stashType;
    private boolean itemError;
    private boolean pageError;

    public GDStashPage(final InputStream reader, final int stashType, final int containerVersion, final int w, final boolean hardcore, final String filename, final int page, final GDLog log) {
        super(1);
        this.itemError = false;
        this.pageError = false;
        this.reader = reader;
        this.filename = filename;
        this.stashType = stashType;
        this.width = w;
        if (stashType == 0) {
            this.readOldFormat(hardcore, page + 1, containerVersion);
        }
        if (stashType == 1) {
            this.readNewFormat(hardcore, page + 1, containerVersion, log);
        }
    }

    public boolean hasItemErrors() {
        return this.itemError;
    }

    public boolean hasPageErrors() {
        return this.pageError;
    }

    private void readNewFormat(final boolean hardcore, final int page, final int containerVersion, final GDLog log) {
        try {
            int val = 0;
            final GDReader.Block block = new GDReader.Block();
            val = GDReader.readBlockStart(block);
            this.width = GDReader.readEncInt(true);
            this.height = GDReader.readEncInt(true);
            final int numItems = GDReader.readEncInt(true);
            this.items.clear();
            this.removedItems.clear();
            for (int i = 0; i < numItems; ++i) {
                final GDItem item = new GDItem(this.reader, this.stashType, containerVersion, hardcore, this.filename, page, log);
                if (!item.hasErrors()) {
                    this.items.add(item);
                } else {
                    this.removedItems.add(item);
                    this.itemError = true;
                }
            }
            GDReader.readBlockEnd(block);
            this.changed = false;
        } catch (Exception ex) {
            this.pageError = true;
            GDMsgLogger.addError(ex);
        }
    }

    private void readOldFormat(final boolean hardcore, final int page, final int containerVersion) {
        try {
            int numItems = 0;
            int len = 0;
            while (len != -1) {
                len = GDReader.readUInt(this.reader);
                if (len != -1) {
                    final String s = GDReader.readString(this.reader, len);
                    if (s.equals("sackWidth")) {
                        this.width = GDReader.readInt(this.reader);
                    }
                    if (s.equals("sackHeight")) {
                        this.height = GDReader.readInt(this.reader);
                    }
                    if (s.equals("numItems")) {
                        numItems = GDReader.readUInt(this.reader);
                        break;
                    }
                    continue;
                }
            }
            this.items.clear();
            for (int i = 0; i < numItems; ++i) {
                final GDItem item = new GDItem(this.reader, this.stashType, containerVersion, hardcore, this.filename, page, null);
                if (!item.hasErrors()) {
                    this.items.add(item);
                } else {
                    this.itemError = true;
                }
            }
            this.removedItems.clear();
            this.changed = false;
        } catch (IOException ex) {
            this.pageError = true;
        }
    }

    public int getByteSize(final int containerVersion) {
        int size = 0;
        size += 4;
        size += 4;
        size += 4;
        for (final GDItem item : this.items) {
            size += item.getByteSize(containerVersion);
        }
        for (final GDItem item : this.removedItems) {
            size += item.getByteSize(containerVersion);
        }
        return size;
    }

    public void write(final int containerVersion) throws IOException {
        final GDReader.Block block = new GDReader.Block();
        GDWriter.writeBlockStart(block, 0);
        GDWriter.writeInt(this.width);
        GDWriter.writeInt(this.height);
        final int val = this.items.size() + this.removedItems.size();
        GDWriter.writeInt(val);
        for (final GDItem item : this.items) {
            item.write(containerVersion);
        }
        for (final GDItem item : this.removedItems) {
            item.write(containerVersion);
        }
        GDWriter.writeBlockEnd(block);
        this.changed = false;
    }

    public void writeOldFormat(final FileOutputStream writer, final Charset cs) throws IOException {
        writer.write(GDWriter.lengthToBytes4("sackWidth"));
        writer.write("sackWidth".getBytes(cs));
        writer.write(GDWriter.intToBytes4(this.width));
        writer.write(GDWriter.lengthToBytes4("sackHeight"));
        writer.write("sackHeight".getBytes(cs));
        writer.write(GDWriter.intToBytes4(this.height));
        writer.write(GDWriter.lengthToBytes4("numItems"));
        writer.write("numItems".getBytes(cs));
        final int val = this.items.size() + this.removedItems.size();
        GDWriter.writeInt(val);
        writer.write(GDWriter.intToBytes4(val));
        for (final GDItem item : this.items) {
            item.writeOldFormat(writer, cs);
        }
        for (final GDItem item : this.removedItems) {
            item.writeOldFormat(writer, cs);
        }
        this.changed = false;
    }
}
